package com.joshondesign.sdljava.gfx;

import com.joshondesign.sdljava.SDL;
import com.joshondesign.sdljava.SDL_Surface;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * This is an implementation of GFX which adds additional drawing features
 * implemented in pure java, such as drawRect
 */
public class JavaExtendedGFX extends GFX {

    public JavaExtendedGFX(SDL_Surface surface) {
        super(surface);
    }

    public void drawRect(int x, int y, int width, int height) {

        //h lines
        for (int i = 0; i < width + 1; i++) {
            setPixel(x + i, y);
            setPixel(x + i, y+height);
        }
        for (int j=0; j<height;j++) {
            setPixel(x,y+j);
            setPixel(x+width,y+j);
        }

        /*
        SDL.SDL_LockSurface(surface);

        Object buffer = SDL.SWIG_getPixelDirectByteBuffer(surface);
        ByteBuffer byteBuffer = (ByteBuffer) buffer;

        int sw = surface.getW();

        byteBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        if (y >= clipRect.getY() && y <= clipRect.getY() + clipRect.getHeight()) {
            //top line
            byteBuffer.position(x * 4 + sw * y * 4);
            for (int i = 0; i < width + 1; i++) {
                if (insideClip(x, y)) {
                    byteBuffer.putInt((int) current_sdlcolor);
                } else {
                    byteBuffer.putInt(byteBuffer.getInt());
                }
            }
        }


        for (int i = 0; i < height + 1; i++) {
            if (y + i >= clipRect.getY() && y + i <= clipRect.getY() + clipRect.getHeight()) {
                byteBuffer.position(x * 4 + sw * (y + i) * 4);
                if (insideClip(x, y + i)) {
                    byteBuffer.putInt((int) current_sdlcolor);
                }
                byteBuffer.position(byteBuffer.position() + width * 4);
                if (insideClip(x + width, y + i)) {
                    byteBuffer.putInt((int) current_sdlcolor);
                }
            }
        }


        //bottom line
        if (y + height >= clipRect.getY() && y + height <= clipRect.getY() + clipRect.getHeight()) {
            byteBuffer.position(x * 4 + sw * (y + height) * 4);
            for (int i = 0; i < width + 1; i++) {
                if (insideClip(x, y + height)) {
                    byteBuffer.putInt((int) current_sdlcolor);
                }
            }
        }
        SDL.SDL_UnlockSurface(surface);
        */
    }

    private boolean insideClip(int x, int y) {
        if (x >= clipRect.getX()
                && y >= clipRect.getY()
                && x <= clipRect.getX() + clipRect.getWidth()
                && y <= clipRect.getY() + clipRect.getHeight()) {
            return true;
        }
        return false;
    }


    public void drawHLine(int x, int y, int width) {
        /*
        SDL.SDL_LockSurface(surface);
        Object buffer = SDL.SWIG_getPixelDirectByteBuffer(surface);
        ByteBuffer byteBuffer = (ByteBuffer) buffer;

        int sw = surface.getW();
        byteBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.position(x*4 + sw*y*4);
        for(int i=0; i<width+1; i++) {
            byteBuffer.putInt((int) current_sdlcolor);
        }
        SDL.SDL_UnlockSurface(surface);
        */
        for (int i = 0; i < width + 1; i++) {
            setPixel(x + i, y);
        }
    }

    public void drawVLine(int x, int y, int height) {
        /*
        SDL.SDL_LockSurface(surface);
        Object buffer = SDL.SWIG_getPixelDirectByteBuffer(surface);
        ByteBuffer byteBuffer = (ByteBuffer) buffer;

        int sw = surface.getW();
        byteBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        for(int i=0; i<height+1; i++) {
            byteBuffer.position(x*4 + sw*(y+i)*4);
            byteBuffer.putInt((int) current_sdlcolor);
        }
        SDL.SDL_UnlockSurface(surface);
        */
        for (int i = 0; i < height + 1; i++) {
            setPixel(x, y + i);
        }
    }

    //code from http://free.pages.at/easyfilter/bresenham.html
    //Alois Zingl, Vienna, Austria
    public void drawLine(int x0, int y0, int x1, int y1) {
        int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
        int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
        int err = dx + dy, e2; /* error value e_xy */

        for (; ; ) {  /* loop */
            setPixel(x0, y0);
            if (x0 == x1 && y0 == y1) break;
            e2 = 2 * err;
            if (e2 >= dy) {
                err += dy;
                x0 += sx;
            } /* e_xy+e_x > 0 */
            if (e2 <= dx) {
                err += dx;
                y0 += sy;
            } /* e_xy+e_y < 0 */
        }
    }

    private void setPixel(int x0, int y0) {
        if (currentPaint != currentColor) {
            SDL.setPixel(this.surface, x0+translateX, y0+translateY, getPixelValue(x0, y0, currentPaint.getImage(surface)));
        } else {
            SDL.setPixel(this.surface, x0+translateX, y0+translateY, current_sdlcolor);
        }
    }

    private long getPixelValue(int x0, int y0, SDL_Surface image) {
        return SDL.getPixel(image, x0 % image.getW(), y0 % image.getH());
    }

    public void drawCircle(GFX gfx, int cx, int cy, int radius) {
        int x = -radius, y = 0, err = 2 - 2 * radius; /* II. Quadrant */
        do {
            SDL.setPixel((SDL_Surface) gfx.getNative(), cx - x, cy + y, current_sdlcolor); /*   I. Quadrant */
            SDL.setPixel((SDL_Surface) gfx.getNative(), cx - y, cy - x, current_sdlcolor); /*  II. Quadrant */
            SDL.setPixel((SDL_Surface) gfx.getNative(), cx + x, cy - y, current_sdlcolor); /* III. Quadrant */
            SDL.setPixel((SDL_Surface) gfx.getNative(), cx + y, cy + x, current_sdlcolor); /*  IV. Quadrant */
            radius = err;
            if (radius > x) err += ++x * 2 + 1; /* e_xy+e_x > 0 */
            if (radius <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
        } while (x < 0);
    }

    public void drawRoundRect(int x1, int y1, int w, int h, int r) {
        int width = w - r - r;
        int height = h - r - r;
        int xm = x1 + r;
        int ym = y1 + r;
        int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
        do {
            setPixel(xm - x + width, ym + y + height); /*   I. Quadrant */
            setPixel(xm - y, ym - x + height); /*  II. Quadrant */
            setPixel(xm + x, ym - y); /* III. Quadrant */
            setPixel(xm + y + width, ym + x); /*  IV. Quadrant */
            r = err;
            if (r > x) err += ++x * 2 + 1; /* e_xy+e_x > 0 */
            if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
        } while (x < 0);
        drawVLine(x1, y1 + r / 2 + 1, h - r);
        drawVLine(x1 + w, y1 + r / 2, h - r);

        drawHLine(x1 + r / 2, y1, w - r);
        drawHLine(x1 + r / 2 + 1, y1 + h, w - r);
    }

    public void fillRoundRect(int x1, int y1, int w, int h, int r) {
        int width = w - r - r;
        int height = h - r - r;
        int xm = x1 + r;
        int ym = y1 + r;
        int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
        do {
            setPixel(xm - x + width, ym + y + height); /*   I. Quadrant */
            setPixel(xm - y, ym - x + height); /*  II. Quadrant */
            setPixel(xm + x, ym - y); /* III. Quadrant */
            setPixel(xm + y + width, ym + x); /*  IV. Quadrant */
            int x2 = xm-x+width;
            int w2 = x2-(xm+x);
            drawHLine(xm+x,ym-y,w2);
            drawHLine(xm+x,ym+y+height,w2);
            r = err;
            if (r > x) err += ++x * 2 + 1; /* e_xy+e_x > 0 */
            if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */
        } while (x < 0);
        drawVLine(x1, y1 + r / 2 + 1, h - r);
        drawVLine(x1 + w, y1 + r / 2, h - r);

        drawHLine(x1 + r / 2, y1, w - r);
        drawHLine(x1 + r / 2 + 1, y1 + h, w - r);
        for(int i=y1+r/2; i<y1+h-r/2; i++) {
            drawHLine(x1,i,w);
        }
    }

    public void drawEllipse(int x1, int y1, int w, int h) {
        int x2 = x1 + w;
        int y2 = y1 + h;
        int a = abs(x1 - x2), b = abs(y1 - y2), b1 = b & 1; /* values of diameter */
        long dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */
        long err = dx + dy + b1 * a * a, e2; /* error of 1.step */

        if (x2 > x1) {
            x2 = x1;
            x1 += a;
        } /* if called with swapped points */
        if (y2 > y1) y2 = y1; /* .. exchange them */
        y2 += (b + 1) / 2;
        y1 = y2 - b1;   /* starting pixel */
        a *= 8 * a;
        b1 = 8 * b * b;

        do {
            setPixel(x1, y2); /*   I. Quadrant */
            setPixel(x2, y2); /*  II. Quadrant */
            setPixel(x2, y1); /* III. Quadrant */
            setPixel(x1, y1); /*  IV. Quadrant */
            e2 = 2 * err;
            if (e2 >= dx) {
                x2++;
                x1--;
                err += dx += b1;
            } /* x step */
            if (e2 <= dy) {
                y2++;
                y1--;
                err += dy += a;
            }  /* y step */
        } while (x2 <= x1);

        while (y2 - y1 < b) {  /* too early stop of flat ellipses a=1 */
            setPixel(x2 - 1, y2); /* -> finish tip of ellipse */
            setPixel(x1 + 1, y2++);
            setPixel(x2 - 1, y1);
            setPixel(x1 + 1, y1--);
        }
    }

    public void fillEllipse(int x1, int y1, int w, int h) {
        int x2 = x1 + w;
        int y2 = y1 + h;
        int a = abs(x1 - x2), b = abs(y1 - y2), b1 = b & 1; /* values of diameter */
        long dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */
        long err = dx + dy + b1 * a * a, e2; /* error of 1.step */

        if (x2 > x1) {
            x2 = x1;
            x1 += a;
        } /* if called with swapped points */
        if (y2 > y1) y2 = y1; /* .. exchange them */
        y2 += (b + 1) / 2;
        y1 = y2 - b1;   /* starting pixel */
        a *= 8 * a;
        b1 = 8 * b * b;

        do {
            setPixel(x1, y2); /*   I. Quadrant */
            setPixel(x2, y2); /*  II. Quadrant */
            setPixel(x2, y1); /* III. Quadrant */
            setPixel(x1, y1); /*  IV. Quadrant */
            drawHLine(x2, y1, x1 - x2);
            drawHLine(x2,y2,x1-x2);
            e2 = 2 * err;
            if (e2 >= dx) {
                x2++;
                x1--;
                err += dx += b1;
            } /* x step */
            if (e2 <= dy) {
                y2++;
                y1--;
                err += dy += a;
            }  /* y step */
        } while (x2 <= x1);

        while (y2 - y1 < b) {  /* too early stop of flat ellipses a=1 */
            setPixel(x2 - 1, y2); /* -> finish tip of ellipse */
            setPixel(x1 + 1, y2++);
            setPixel(x2 - 1, y1);
            setPixel(x1 + 1, y1--);
        }
    }

    private int abs(int i) {
        if (i < 0) {
            return -i;
        }
        return i;
    }

    public void fillRect(int x, int y, int w, int h) {
        if (currentPaint instanceof SColor) {
            super.fillRect(x, y, w, h);
            return;
        }

        SDL_Surface image = currentPaint.getImage(surface);
        for (int i = x; i < x + w; i++) {
            for (int j = y; j < y + h; j++) {
                //drawImage(image,i,y);
                setPixel(i, j);
            }
        }

    }


    @Override
    public void drawWuPixel(double wx, double wy) {
        double wb = 200;
        int x = (int) wx;
        int y = (int) wy;
        double fx = wx-x;
        double fy = wy-y;
        int btl = (int) ((1-fx) * (1-fy)*wb);
        int btr = (int) ((fx)   * (1-fy)*wb);
        int bbl = (int) ((1-fx) * (fy)*wb);
        int bbr = (int) ((fx)   * (fy)*wb);

        //Util.p("btl = " + btl);
        setBWPixel(x,y,btl);
        setBWPixel(x+1,y,btr);
        setBWPixel(x,y+1,bbl);
        setBWPixel(x+1,y+1,bbr);
    }

    private void setBWPixel(int x, int y, int btl) {
        //btl = 255;
        int color = (btl<<24)+(btl<<16)+(btl<<8)+(0XFF<<0);
        SDL.setPixel(this.surface, x+translateX, y+translateY, color);
    }


}
